﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Experiments.Repositories.DataApis
{
    public static class QueryableExtensions
    {
        private static readonly MethodInfo GenericMethod;

        static QueryableExtensions()
        {
            var methods = typeof(QueryableExtensions).GetMethods(BindingFlags.Public | BindingFlags.Static)
                .Where(m =>
                    m.Name == "ODataFilter"
                    && m.IsGenericMethod
                    && m.GetParameters().Count() == 2
                    && m.GetParameters()[0].ParameterType.IsGenericType
                    && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
                    && m.GetParameters()[1].ParameterType == typeof(NameValueCollection));

            GenericMethod = methods.Single();
        }

        /// <summary>
        /// http://www.odata.org/documentation/odata-v2-documentation/uri-conventions/
        /// </summary>
        public static IQueryable<TEntityBase> ODataFilter<TEntityBase>(
            this IQueryable<TEntityBase> entities, Type typeOfEntity, NameValueCollection query)
            where TEntityBase : class
        {
            var specificMethod = GenericMethod.MakeGenericMethod(new[] { typeOfEntity });

            var result = specificMethod.Invoke(null, new object[] { entities, query }) as IQueryable<TEntityBase>;
            return result;
        }

        /// <summary>
        /// http://www.odata.org/documentation/odata-v2-documentation/uri-conventions/
        /// </summary>
        public static IQueryable<TEntity> ODataFilter<TEntity>(
            this IQueryable<TEntity> entities, NameValueCollection query)
            where TEntity : class
        {
            if (!entities.Any() || query.Count == 0)
                return entities;

            return Linq2Rest.ModelFilterExtensions.Filter(entities, query).OfType<TEntity>();
        }
    }
}
